﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics;

namespace BITS.Copy
{
    public class BITSSambaHelper
    {
        public static void Copy(string source, string destination)
        {
            IBackgroundCopyManager manager = (IBackgroundCopyManager)new BackgroundCopyManager();

            try
            {
                Guid jobID;
                IBackgroundCopyJob job;
                manager.CreateJob("copy from " + Path.GetFileName(source) , BG_JOB_TYPE.BG_JOB_TYPE_DOWNLOAD, out jobID, out job);
                
                try
                {
                    //Add content
                    string destinationDir = Add(source, destination, job);
                    job.SetDescription(destinationDir);

                    //notify finish
                    NotifyFinish(job);
                    
                    //Start job
                    job.Resume();
                }
                catch
                {
                    job.Cancel();
                    throw;
                }
                finally
                {
                    if (job != null) Marshal.ReleaseComObject(job);
                }
            }
            finally
            {
                if (manager != null)
                {
                    Marshal.ReleaseComObject(manager);
                }
            }
        }

        private static void NotifyFinish(IBackgroundCopyJob job)
        {            
            IBackgroundCopyJob2 job2 = job as IBackgroundCopyJob2;
            if (job2 != null)
            {
                string fileName = Assembly.GetExecutingAssembly().Location;
                Guid jobIDn;
                job.GetId(out jobIDn);
                job2.SetNotifyCmdLine(fileName, fileName + " -complete " + jobIDn.ToString("N"));
            }
            else
            {
                throw new InvalidOperationException("Invalid BITS version. Request 3.0 or new");
            }
        }

        private static string Add(string source, string destination, IBackgroundCopyJob job)
        {
            if (Directory.Exists(source))
            {
                destination = Path.Combine(destination, "$" + Path.GetFileName(source));
                if (!Directory.Exists(destination))
                {
                    Directory.CreateDirectory(destination);
                }

                string[] dir = Directory.GetDirectories(source);
                foreach (var item in dir)
                {
                    Add(item, destination, job);
                }

                string[] files = Directory.GetFiles(source);
                foreach (var item in files)
                {
                    Add(item, Path.Combine(destination, Path.GetFileName(item)), job);
                }

                return destination;

            }
            else
            {
                if (File.Exists(source))
                {
                    if (Directory.Exists(destination))
                    {
                        AddFile(source, Path.Combine(destination, Path.GetFileName(source)), job);
                    }
                    else
                    {
                        AddFile(source, destination, job);
                    }
                }
                else
                {
                    throw new InvalidOperationException("Source does not exists.");
                }
            }

            return string.Empty;
        }

        private static void AddFile(string source, string destination, IBackgroundCopyJob job)
        {
            job.AddFile(source, destination);
        }

        public static void CompleteJob(Guid jobID)
        {
            IBackgroundCopyManager manager = (IBackgroundCopyManager)new BackgroundCopyManager();
            IBackgroundCopyJob job = null;

            try
            {
                manager.GetJob(ref jobID, out job);

                string destination;
                job.GetDescription(out destination);

                BG_JOB_STATE state;
                job.GetState(out state);
                if (state == BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED)
                {
                    job.Complete();
                }
                else
                {
                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("Job ID:'{0}' is in state : {1}", jobID.ToString("N"), state.ToString());
                    sb.AppendLine();
                    sb.AppendFormat("Choose action:  [R]esume, [S]uspend, [C]omplete, C[a]ncel. Press key.");
                    sb.AppendLine();
                    Console.WriteLine(sb.ToString());
                    ConsoleKeyInfo key = Console.ReadKey();

                    switch (char.ToUpper(key.KeyChar))
                    {
                        case 'R': job.Resume(); NotifyFinish(job); break;
                        case 'S': job.Suspend(); NotifyFinish(job); break;
                        case 'C': 
                            {
                                job.Complete();
                                if (!string.IsNullOrWhiteSpace(destination))
                                {
                                    CompleteDirectory(destination);
                                }
                                break;
                            }
                        case 'A':
                            {
                                job.Cancel(); ;
                                if (!string.IsNullOrWhiteSpace(destination))
                                {
                                    CompleteDirectory(destination);
                                }
                                break;
                            }
                    }
                }

                //IEnumBackgroundCopyFiles fileEnum = null;
                //try
                //{
                //    job.EnumFiles(out fileEnum);

                //    uint count;
                //    fileEnum.GetCount(out count);
                //    for (int i = 0; i < count; i++)
                //    {
                //        IBackgroundCopyFile file = null;
                //        try
                //        {
                //            uint pceltFetched;
                //            string fileName;
                //            fileEnum.Next(1, out file, out pceltFetched);
                //            file.GetLocalName(out fileName);
                //        }
                //        finally
                //        {
                //            if (file != null) Marshal.ReleaseComObject(file);
                //        }
                //    }
                //}
                //finally
                //{
                //    if (fileEnum != null) Marshal.ReleaseComObject(fileEnum);
                //}

                
            }
            catch (COMException comEx)
            {
                if ((uint)comEx.ErrorCode == 0x80200001)
                {
                    System.Environment.ExitCode = 3;
                    Console.WriteLine("Job not found. JobID = " + jobID.ToString("N"));
                }
                else
                {
                    throw;
                }
            }
            finally
            {
                if (job != null) Marshal.ReleaseComObject(job);
                if (manager != null) Marshal.ReleaseComObject(manager);
            }
        }

        private static void CompleteDirectory(string destination)
        {
            if (Directory.Exists(destination))
            {
                string[] dirs = Directory.GetDirectories(destination);
                foreach (var item in dirs)
                {
                    CompleteDirectory(item);
                }

                string name = Path.GetFileName(destination);
                if (name.StartsWith("$"))
                {
                    RenameDirectory(destination, name.Substring(1));
                }
            }
        }

        private static void RenameDirectory(string destination, string newName)
        {
            string newDirectoryName = Path.Combine(Path.GetDirectoryName(destination), newName);
            if (Directory.Exists(newDirectoryName))
            {
                RenameDirectory(destination, newName + ".new");
            }
            else
            {
                Directory.Move(destination, newDirectoryName);
            }
        }

    }
}
